package com.bstek.uflo.designer.serializer.impl;

import java.util.ArrayList;
import java.util.List;

import org.dom4j.Element;
import org.springframework.util.StringUtils;

import com.bstek.uflo.designer.model.ComponentAuthority;
import com.bstek.uflo.designer.model.ConditionType;
import com.bstek.uflo.designer.model.FormElement;
import com.bstek.uflo.designer.model.Mapping;
import com.bstek.uflo.designer.model.Point;
import com.bstek.uflo.designer.model.Rectangle;
import com.bstek.uflo.designer.model.Shape;
import com.bstek.uflo.designer.model.edge.Connection;
import com.bstek.uflo.designer.model.node.Node;
import com.bstek.uflo.designer.model.node.RectNode;
import com.bstek.uflo.designer.model.node.Start;
import com.bstek.uflo.designer.model.node.Task;
import com.bstek.uflo.designer.serializer.ShapeSerializer;
import com.bstek.uflo.designer.utils.ShapeUtils;

/**
 * @author matt.yao@bstek.com
 * @since 1.0
 */
public abstract class NodeSerializer implements ShapeSerializer {

	public void execute(Element parentElement, Shape shape) {
		Element currentElement = buildCurrentElement(parentElement, shape);
		serializerCommonAttribute(currentElement, shape);
		serializerPrivateAttribute(currentElement, shape);
		serializerFormElementAttribute(currentElement, shape);
	}

	public abstract void serializerPrivateAttribute(Element currentElement, Shape shape);

	protected void serializerCommonAttribute(Element currentElement, Shape shape) {
		Node node = (Node) shape;
		currentElement.addAttribute("g", buildNodeGValue(node));
		String name = node.getName();
		if (StringUtils.hasText(name)) {
			currentElement.addAttribute("name", name);
		}
		String eventHandlerBean = node.getEventHandlerBean();
		if (StringUtils.hasText(eventHandlerBean)) {
			currentElement.addAttribute("event-handler-bean", eventHandlerBean);
		}
		String desc = node.getDescription();
		if (StringUtils.hasText(desc)) {
			Element descriptionElement = currentElement.addElement("description");
			descriptionElement.addText(desc);
		}
		this.serializerSequenceFlowAttribute(currentElement, node);
		this.serializerComponentAuthorityAttribute(currentElement, node);

	}

	private void serializerSequenceFlowAttribute(Element currentElement, Node node) {
		List<Connection> connecitons = node.getOutConnections();
		if (connecitons != null) {
			for (Connection conn : connecitons) {
				Element connectionElement = currentElement.addElement("sequence-flow");
				connectionElement.addAttribute("name", conn.getName());
				connectionElement.addAttribute("to", conn.getToNode().getName());
				String conditionType = conn.getConditionType();
				if (StringUtils.hasText(conditionType)) {
					if (conditionType.equals(ConditionType.Expression.name())) {
						connectionElement.addAttribute("condition-type", conn.getConditionType());
						connectionElement.addAttribute("expression", conn.getExpression());
					} else if (conditionType.equals(ConditionType.Handler.name())) {
						connectionElement.addAttribute("condition-type", conn.getConditionType());
						connectionElement.addAttribute("handler-bean", conn.getHandlerBean());
					}
				}
				connectionElement.addAttribute("g", this.buildConnectionGValue(conn));
			}
		}
	}

	private void serializerComponentAuthorityAttribute(Element currentElement, Node node) {
		List<ComponentAuthority> componentAuthorities = null;
		if (node instanceof Task) {
			componentAuthorities = ((Task) node).getComponentAuthorities();
		} else if (node instanceof Start) {
			componentAuthorities = ((Start) node).getComponentAuthorities();
		}
		if (componentAuthorities != null) {
			for (ComponentAuthority componentAuthority : componentAuthorities) {
				Element componentAuthorityElement = currentElement.addElement("component-authority");
				componentAuthorityElement.addAttribute("authority", componentAuthority.getAuthority());
				componentAuthorityElement.addAttribute("component", componentAuthority.getComponent());
			}
		}
	}

	private String buildNodeGValue(Node node) {
		Rectangle rectangle = node.getRectangle();
		int x = rectangle.getPoint().getX();
		int y = rectangle.getPoint().getY();
		int width = rectangle.getWidth();
		int height = rectangle.getHeight();
		x = x - 50;
		y = y - 25;
		if (node instanceof RectNode) {
			height = height + 15;
		} else {
			y = y - 5;
			x = x + 40;
			height = height + 30;
			width = width + 8;
		}
		return x + "," + y + "," + width + "," + height;

	}

	private String buildConnectionGValue(Connection conn) {
		List<Point> points = conn.getVertices();
		points.remove(0);
		points.remove(points.size() - 1);
		List<String> pointString = new ArrayList<String>();
		for (Point point : points) {
			int pointX = point.getX() + 10;
			int pointY = point.getY();
			pointString.add(pointX + "," + pointY);
		}
		String value = org.apache.commons.lang.StringUtils.join(pointString, ";") + ":-40,-9";
		return value;
	}

	protected void serializerFormElementAttribute(Element currentElement, Shape shape) {
		String formTemplate = null;
		List<FormElement> formElements = null;
		if (shape instanceof Start) {
			formTemplate = ((Start) shape).getFormTemplate();
			formElements = ((Start) shape).getFormElements();
		} else if (shape instanceof Task) {
			formTemplate = ((Task) shape).getFormTemplate();
			formElements = ((Task) shape).getFormElements();
		}
		if (StringUtils.hasText(formTemplate)) {
			currentElement.addAttribute("form-template", formTemplate);
		}
		if (formElements != null) {
			for (FormElement formElement : formElements) {
				Element formElementElement = currentElement.addElement("form-element");
				String name = formElement.getName();
				if (StringUtils.hasText(name)) {
					formElementElement.addAttribute("name", name);
				}
				String caption = formElement.getCaption();
				if (StringUtils.hasText(caption)) {
					formElementElement.addAttribute("caption", caption);
				}
				String dataType = formElement.getDataType();
				if (StringUtils.hasText(dataType)) {
					formElementElement.addAttribute("data-type", dataType);
				}
				String editorType = formElement.getEditorType();
				if (StringUtils.hasText(editorType)) {
					formElementElement.addAttribute("editor-type", editorType);
				}
				String authority = formElement.getAuthority();
				if (StringUtils.hasText(authority)) {
					formElementElement.addAttribute("authority", authority);
				}
				formElementElement.addAttribute("required", String.valueOf(formElement.isRequired()));
				String defaultValue = formElement.getDefaultValue();
				if (StringUtils.hasText(defaultValue)) {
					formElementElement.addAttribute("default-value", defaultValue);
				}
				List<Mapping> mappings = formElement.getMappings();
				if (mappings != null) {
					for (Mapping mapping : mappings) {
						Element mappingElement = formElementElement.addElement("mapping");
						mappingElement.addAttribute("key", mapping.getKey());
						mappingElement.addAttribute("label", mapping.getLabel());
					}
				}
			}
		}
	}

	private Element buildCurrentElement(Element parentElement, Shape shape) {
		String nodeName = ShapeUtils.getShapXmlName(shape);
		Element element = parentElement.addElement(nodeName);
		return element;
	}

	public boolean support(Shape shape) {
		return shape instanceof Node ? true : false;
	}

}
